/* Sentinel - IRC Statistical and Operator Services
** bconf.c - Configuration implementation for the csircd derived bind
**           style conf
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: bconf.c 10 2010-02-02 01:12:49Z dubkat $
*/

#include "stats.h"
#include "struct.h"
#include "proto.h"
#include "token.h"
#include "flags.h"
#include "match.h"
#include "slog.h"
#include "mem.h"
#include "configparse.h"

#define PARAM_ERROR(ce, s)	{ slog(DEFAULT_LOG, L_WARNING,"%s:%i: no parameter for %s " \
                                    "config option: %s", \
                                    (ce)->ce_fileptr->cf_filename, \
                                    (ce)->ce_varlinenum, (s), (ce)->ce_varname); \
  return 1; }

#ifdef STATSERV
static void AddStatsChannel(char *);
#endif

#ifdef SPLITSERV
static struct Token split_options[] =
  {
    { "OPEN", SO_OPEN },
    { "OPERONLY", SO_OPERONLY },
    { "DISABLED", SO_DISABLED },
    { NULL, TOKEN_ERROR }
  };
#endif

struct ConfTable
{
  char *name;
  int rehashable;
  int (*handler)(CONFIGENTRY *);
};

static int generic_subblock_handler(CONFIGENTRY *, char *, struct ConfTable *);

static int c_serverinfo(CONFIGENTRY *);
static int c_statserv(CONFIGENTRY *);
static int c_monserv(CONFIGENTRY *);
static int c_adminserv(CONFIGENTRY *);
static int c_hidden(CONFIGENTRY *);
static int c_flood(CONFIGENTRY *);
static int c_html(CONFIGENTRY *);
static int c_datadump(CONFIGENTRY *);
static int c_splitserv(CONFIGENTRY *);
static int c_jupeserv(CONFIGENTRY *);
static int c_listserv(CONFIGENTRY *);
static int c_operator(CONFIGENTRY *);
static int c_drones(CONFIGENTRY *);
static struct ConfTable conf_root_table[] =
  {
    { "SERVERINFO", 1, c_serverinfo },
    { "STATSERV", 1, c_statserv },
    { "MONSERV", 1, c_monserv },
    { "ADMINSERV", 1, c_adminserv },
    { "HIDDEN", 1, c_hidden },
    { "FLOOD", 1, c_flood },
    { "HTML", 1, c_html },
    { "DATADUMP", 1, c_datadump },
    { "SPLITSERV", 1, c_splitserv },
    { "JUPESERV", 1, c_jupeserv },
    { "LISTSERV", 1, c_listserv },
    { "OPERATOR", 1, c_operator },
    { "DRONES", 1, c_drones },
    { NULL, 0, NULL }
  };

static int c_si_name(CONFIGENTRY *);
static int c_si_sid(CONFIGENTRY * );
static int c_si_port(CONFIGENTRY *);
static int c_si_uplink(CONFIGENTRY *);
/* XXX Create a subblock of uplinks */
static int c_si_pass(CONFIGENTRY *);
static int c_si_vhost(CONFIGENTRY *);
static int c_si_desc(CONFIGENTRY *);
static int c_si_recontime(CONFIGENTRY *);
static int c_si_silent(CONFIGENTRY *);
static int c_si_notifyeob(CONFIGENTRY *);
static int c_si_netname(CONFIGENTRY *);
static int c_si_adminname(CONFIGENTRY *);
static int c_si_adminemail(CONFIGENTRY *);
static int c_si_ignoreillegaltld(CONFIGENTRY *);
static int c_si_loglevel(CONFIGENTRY *);
static int c_si_localip(CONFIGENTRY *);
static struct ConfTable conf_serverinfo_table[] =
  {
    { "NAME", 0, c_si_name },
    { "SID", 0, c_si_sid },
    { "PORT", 0, c_si_port },
    { "UPLINK", 0, c_si_uplink },
    { "PASS", 0, c_si_pass },
    { "VHOST", 0, c_si_vhost },
    { "DESC", 0, c_si_desc },
    { "RECONTIME", 1, c_si_recontime },
    { "SILENT", 1, c_si_silent },
    { "NOTIFYEOB", 1, c_si_notifyeob },
    { "NETNAME", 1, c_si_netname },
    { "ADMINNAME", 1, c_si_adminname },
    { "ADMINEMAIL", 1, c_si_adminemail },
    { "IGNOREILLEGALTLD", 1, c_si_ignoreillegaltld },
    { "LOGLEVEL", 1, c_si_loglevel },
    { "LOCALIP", 1, c_si_localip },
    { NULL, 0, NULL }
  };

static int c_st_nick(CONFIGENTRY *);
static int c_st_user(CONFIGENTRY *);
static int c_st_host(CONFIGENTRY *);
static int c_st_desc(CONFIGENTRY *);
static int c_st_operonly(CONFIGENTRY *);
static int c_st_setoper(CONFIGENTRY *);
static int c_st_channels(CONFIGENTRY *);
static int c_st_publicchannel(CONFIGENTRY *);
static int c_st_publicdelayed(CONFIGENTRY *);
static struct ConfTable conf_statserv_table[] =
  {
    { "NICK", 0, c_st_nick },
    { "USER", 0, c_st_user },
    { "HOST", 0, c_st_host },
    { "DESC", 0, c_st_desc },
    { "OPERONLY", 1, c_st_operonly },
    { "SETOPER", 0, c_st_setoper },
    { "CHANNELS", 0, c_st_channels },
    { "PUBLICCHANNEL", 0, c_st_publicchannel },
    { "PUBLICDELAYED", 1, c_st_publicdelayed },
    { NULL, 0, NULL }
  };

static int c_ms_nick(CONFIGENTRY *);
static int c_ms_user(CONFIGENTRY *);
static int c_ms_host(CONFIGENTRY *);
static int c_ms_desc(CONFIGENTRY *);
static int c_ms_ignoreservers(CONFIGENTRY *);
static struct ConfTable conf_monserv_table[] =
  {
    { "NICK", 0, c_ms_nick },
    { "USER", 0, c_ms_user },
    { "HOST", 0, c_ms_host },
    { "DESC", 0, c_ms_desc },
    { "IGNORESERVERS", 1, c_ms_ignoreservers },
    { "DRONES", 1, c_drones },
    { NULL, 0, NULL }
  };

#if defined(MONSERV) && defined(MON_DRONES)
static int c_ms_dr_gecos(CONFIGENTRY *);
static int c_ms_dr_nick(CONFIGENTRY *);
static int c_ms_dr_username(CONFIGENTRY *);
static int c_ms_dr_channel(CONFIGENTRY *);
static int c_ms_dr_version(CONFIGENTRY *);
static int c_ms_dr_nickgecos(CONFIGENTRY *);
static int c_ms_dr_trackfizzer(CONFIGENTRY *);
static struct ConfTable conf_ms_drones_table[] =
  {
    { "GECOS", 1, c_ms_dr_gecos },
    { "NICK", 1, c_ms_dr_nick },
    { "USERNAME", 1, c_ms_dr_username },
    { "CHANNEL", 1, c_ms_dr_channel },
    { "VERSION", 1, c_ms_dr_version },
    { "NICKGECOS", 1, c_ms_dr_nickgecos },
    { "TRACKFIZZER", 1, c_ms_dr_trackfizzer },
    { NULL, 0, NULL }
  };
#endif

static int c_as_nick(CONFIGENTRY *);
static int c_as_user(CONFIGENTRY *);
static int c_as_host(CONFIGENTRY *);
static int c_as_desc(CONFIGENTRY *);
static int c_as_operonly(CONFIGENTRY *);
static int c_as_defaultflags(CONFIGENTRY *);
static int c_as_unauthchat(CONFIGENTRY *);
static int c_as_dccoperonly(CONFIGENTRY *);
static int c_as_dcclowport(CONFIGENTRY *);
static int c_as_dcchighport(CONFIGENTRY *);
static int c_as_spywallops(CONFIGENTRY *);
static int c_as_opersadminonly(CONFIGENTRY *);
static int c_as_defaultlimit(CONFIGENTRY *);
static struct ConfTable conf_adminserv_table[] =
  {
    { "NICK", 0, c_as_nick },
    { "USER", 0, c_as_user },
    { "HOST", 0, c_as_host },
    { "DESC", 0, c_as_desc },
    { "OPERONLY", 1, c_as_operonly },
    { "DEFAULTFLAGS", 1, c_as_defaultflags },
    { "UNAUTHCHAT", 1, c_as_unauthchat },
    { "DCCOPERONLY", 1, c_as_dccoperonly },
    { "DCCLOWPORT", 1, c_as_dcclowport },
    { "DCCHIGHPORT", 1, c_as_dcchighport },
    { "SPYWALLOPS", 1, c_as_spywallops },
    { "OPERSADMINONLY", 1, c_as_opersadminonly },
    { "DEFAULTLIMIT", 1, c_as_defaultlimit },
    { NULL, 0, NULL }
  };

static int c_fl_count(CONFIGENTRY *);
static int c_fl_time(CONFIGENTRY *);
static int c_fl_ignore(CONFIGENTRY *);
static struct ConfTable conf_flood_table[] =
  {
    { "COUNT", 1, c_fl_count },
    { "TIME", 1, c_fl_time },
    { "IGNORE", 1, c_fl_ignore },
    { NULL, 0, NULL }
  };

static int c_ht_enable(CONFIGENTRY *);
static int c_ht_filename(CONFIGENTRY *);
static int c_ht_time(CONFIGENTRY *);
static int c_ht_url(CONFIGENTRY *);
static int c_ht_topchannum(CONFIGENTRY *);
static struct ConfTable conf_html_table[] =
  {
    { "ENABLE", 1, c_ht_enable },
    { "FILENAME", 1, c_ht_filename },
    { "TIME", 1, c_ht_time },
    { "URL", 1, c_ht_url },
    { "TOPCHANNUM", 1, c_ht_topchannum },
    { NULL, 0, NULL }
  };

static int c_dd_enable(CONFIGENTRY *);
static int c_dd_filename(CONFIGENTRY *);
static int c_dd_time(CONFIGENTRY *);
static struct ConfTable conf_datadump_table[] =
  {
    { "ENABLE", 1, c_dd_enable },
    { "FILENAME", 1, c_dd_filename },
    { "TIME", 1, c_dd_time },
    { NULL, 0, NULL }
  };

static int c_sp_nick(CONFIGENTRY *);
static int c_sp_user(CONFIGENTRY *);
static int c_sp_host(CONFIGENTRY *);
static int c_sp_desc(CONFIGENTRY *);
static int c_sp_delay(CONFIGENTRY *);
static int c_sp_mapfile(CONFIGENTRY *);
static int c_sp_expiretime(CONFIGENTRY *);
static int c_sp_remindtime(CONFIGENTRY *);
static int c_sp_channel(CONFIGENTRY *);
static int c_sp_mapcommand(CONFIGENTRY *);
static int c_sp_splitcommand(CONFIGENTRY *);
static int c_sp_dumpcommand(CONFIGENTRY *);
static int c_sp_hidehidden(CONFIGENTRY *);
static int c_sp_operonly(CONFIGENTRY *);
static int c_sp_report(CONFIGENTRY *);
static int c_sp_trackondisconnect(CONFIGENTRY *);
static struct ConfTable conf_splitserv_table[] =
  {
    { "NICK", 0, c_sp_nick },
    { "USER", 0, c_sp_user },
    { "HOST", 0, c_sp_host },
    { "DESC", 0, c_sp_desc },
    { "DELAY", 1, c_sp_delay },
    { "MAPFILE", 1, c_sp_mapfile },
    { "EXPIRETIME", 1, c_sp_expiretime },
    { "REMINDTIME", 1, c_sp_remindtime },
    { "CHANNEL", 0, c_sp_channel },
    { "MAPCOMMAND", 1, c_sp_mapcommand },
    { "SPLITCOMMAND", 1, c_sp_splitcommand },
    { "DUMPCOMMAND", 1, c_sp_dumpcommand },
    { "HIDEHIDDEN", 1, c_sp_hidehidden },
    { "OPERONLY", 1, c_sp_operonly },
    { "REPORT", 1, c_sp_report },
    { "TRACKONDISCONNECT", 1, c_sp_trackondisconnect },
    { NULL, 0, NULL }
  };

#ifdef SPLITSERV
static int c_sp_mf_enable(CONFIGENTRY *);
static int c_sp_mf_filename(CONFIGENTRY *);
static int c_sp_mf_time(CONFIGENTRY *);
static struct ConfTable conf_sp_mapfile_table[] =
  {
    { "ENABLE", 1, c_sp_mf_enable },
    { "FILENAME", 1, c_sp_mf_filename },
    { "TIME", 1, c_sp_mf_time },
    { NULL, 0, NULL }
  };
#endif

static int c_js_nick(CONFIGENTRY *);
static int c_js_user(CONFIGENTRY *);
static int c_js_host(CONFIGENTRY *);
static int c_js_desc(CONFIGENTRY *);
static int c_js_persist(CONFIGENTRY *);
static int c_js_votes(CONFIGENTRY *);
static int c_js_alloper(CONFIGENTRY *);
static struct ConfTable conf_jupeserv_table[] =
  {
    { "NICK", 0, c_js_nick },
    { "USER", 0, c_js_user },
    { "HOST", 0, c_js_host },
    { "DESC", 0, c_js_desc },
    { "PERSIST", 1, c_js_persist },
    { "VOTES", 1, c_js_votes },
    { "ALLOPER", 1, c_js_alloper },
    { NULL, 0, NULL }
  };

static int c_ls_nick(CONFIGENTRY *);
static int c_ls_user(CONFIGENTRY *);
static int c_ls_host(CONFIGENTRY *);
static int c_ls_desc(CONFIGENTRY *);
static int c_ls_defaultmax(CONFIGENTRY *);
static struct ConfTable conf_listserv_table[] =
  {
    { "NICK", 0, c_ls_nick },
    { "USER", 0, c_ls_user },
    { "HOST", 0, c_ls_host },
    { "DESC", 0, c_ls_desc },
    { "DEFAULTMAX", 1, c_ls_defaultmax },
    { NULL, 0, NULL }
  };

#ifdef STATSERV
static void AddStatsChannel(char *chan)
{
  char *s = NULL;
  char *pos = chan;
  dlink_node *dl;

  while ( (s = strchr(pos, ',')) )
  {
    *s = '\0';
    s++;
    if (*s == ' ')
      s++;
    dl = dlink_create();
    dlink_add(sstrdup(pos), dl, &me.chan_list);
    pos = s;
  }

  if (pos && (*pos != '\n'))
  {
    dl = dlink_create();
    dlink_add(sstrdup(pos), dl, &me.chan_list);
  }
}
#endif

/* Set default parameters.  The settings used are the same as
** what is provided in stats.cfg.dist except for the binary
** ones, which MUST be default 0 in order for the conf load
** to work properly
*/
void InitDefaults()
{
  /* These strcpy's are safe, as they are static strings */

  /* All bit values must be initialized to 0 */
  strcpy(settings.name, "stats.network.tld");
  strcpy(settings.sid, "000");
  settings.port = 6666;
  strcpy(settings.uplink, "hub.network.tld");
  strcpy(settings.vhost, "0.0.0.0");
  strcpy(settings.pass, "password");
  strcpy(settings.desc, "Network Statistical Services");
  settings.flags = 0x00000000; /* 0 is fine, but be explicit */
  settings.r_time = 10;
  settings.flood = 6;
  settings.flood_time = 10;
#ifdef STATSERV

  strcpy(StatServ.host, "stats.network.tld");
  strcpy(StatServ.user, "stats");
  strcpy(StatServ.nick, "StatServ");
  strcpy(StatServ.desc, "/msg StatServ HELP");
  StatServ.flags = 0;
#endif
#ifdef PUBSTATS

  strcpy(settings.pub_chan, "#statserv");
#endif

  strcpy(settings.html, "/home/user/public_html/stats.html");
  settings.html_time = 3600;
  strcpy(settings.dump_file, "dump.txt");
  settings.dump_time = 3600;
  settings.topchannum = 20;
  strcpy(settings.join_chans, "#opers");
  strcpy(settings.url, "http://www.network.tld/stats.html");
  strcpy(settings.netname, "Misconfigured");
  strcpy(settings.admin_name, "Misconfigured Admin (mis-nick)");
  strcpy(settings.admin_email, "admin@misconfigured.net");
  /* The chan_list is derived from the join_chans...don't set it */
#ifdef SPLITSERV

  strcpy(sp.desc, "Split Services");
  sp.delay = 15;
  strcpy(SplitServ.host, "split.network.tld");
  strcpy(SplitServ.user, "split");
  strcpy(SplitServ.nick, "SplitServ");
  sp.flags = 0;
  sp.text_time = 1800;
  sp.expire_time = 172800L;
  sp.remind_time = 0;
  strcpy(sp.text_name, "map.txt");
  strcpy(sp.join_chan, "#split");
  sp.dump_setting = SO_DISABLED;
  /* The other split settings default to OPEN */
#endif

  strcpy(AdminServ.nick, "AdminServ");
  strcpy(AdminServ.user, "admin");
  strcpy(AdminServ.host, "admin.network.tld");
  strcpy(AdminServ.desc, "Administration Service");
#ifdef JUPESERV

  strcpy(JupeServ.nick, "JupeServ");
  strcpy(JupeServ.user, "jupe");
  strcpy(JupeServ.host, "jupe.network.tld");
  strcpy(JupeServ.desc, "Jupe Service");
  settings.jupevotes = 1;
#endif
#ifdef LISTSERV

  strcpy(ListServ.nick, "ListServ");
  strcpy(ListServ.user, "list");
  strcpy(ListServ.host, "list.network.tld");
  strcpy(ListServ.desc, "Channel Listing and Search Service");
  ListServ.default_max = 60;
#endif
#ifdef MONSERV

  strcpy(MonServ.nick, "SERVICES");
  strcpy(MonServ.user, "services");
  strcpy(MonServ.host, "services.network.tld");
  strcpy(MonServ.desc, "Services IRC Monitor");
#endif

  settings.default_flags = (CF_NOTICES | CF_CHAT);
  settings.dcc_low_port = 3510;
  settings.dcc_high_port = 3590;
  settings.defaultlimit = 60;

  me.local_ip_str[0] = '\0';
}

/* Used to verify setting from the conf during startup and rehash...
** This prevents invalid variables from being used
*/
void VerifyConf()
{
  /* Sanity checking for FLOOD */
  if (settings.flood == 1)
  {
    slog(DEFAULT_LOG, L_ERR, "WARNING: FLOOD set too low, defaulting to 2\n");
    settings.flood = 2;
  }

#ifdef JUPESERV
  if (settings.jupevotes < 1)
  {
    slog(DEFAULT_LOG, L_ERR,
         "jupeserv { jupevotes }; set too low, defaulting to 1.");
    settings.jupevotes = 1;
  }
#endif

#if defined(PUBSTATS) && defined(SPLITSERV)
  if (irccasecmp(settings.pub_chan, sp.join_chan) == 0)
  {
    fprintf(stderr, "FATAL: PUBLIC_CHANNEL and SPLITSERV_CHAN set "
            "to the same value.\nExiting.\n");
    close_logfiles();
    exit(1);
  }
#endif
  if ((settings.dcc_low_port <= 1024) ||
      (settings.dcc_high_port <= 1024) ||
      (settings.dcc_low_port > 65535) ||
      (settings.dcc_high_port > 65535) ||
      (settings.dcc_high_port < settings.dcc_low_port))
  {
    fprintf(stderr, "FATAL: DCC_LOW_PORT and/or DCC_HIGH_PORT set "
            "incorrectly.\nExiting.\n");
    close_logfiles();
    exit(1);
  }
  
  /* verify we have a valid SID set. This is required for TS6 links */
  /* this should do some more error checking --dubkat */
  if ( strlen(settings.sid) != 3 )
  {
	fprintf(stderr, "FATAL: Invalid SID setting [%s].\nSID's must start with a number, 0-9 followed by 2 Alpha-Numeric charactors.\n",
		settings.sid );
	exit(1);
  }
	

  if (IsDebug())
  {
    /* Be sure we are showing all of the logging messages in debug mode */
    set_loglevel(DEFAULT_LOG, L_DEBUG);
  }

  if (me.local_host != NULL)
    free(me.local_host);
  if (me.local_ip_str[0] == '\0')
    me.local_host = get_hostname(NULL);
  else
    me.local_host = get_hostname(me.local_ip_str);
}

void ConfLoad(void)
{
  CONFIGFILE *cfptr;
  CONFIGFILE *cfp;
  CONFIGENTRY *ce;
  struct ConfTable *ct = NULL;

  cfptr = cfp = config_load(CONFIG_NAME);
  if (cfp == NULL)
  {
    fprintf(stderr, "Unable to open configuration file (%s): %s\n",
            CONFIG_NAME, strerror(errno));
    close_logfiles();
    exit(0);
  }
  for (;cfptr;cfptr = cfptr->cf_next) /* Handle include files? */
  {
    for (ce = cfptr->cf_entries;ce;ce = ce->ce_next)
    {
      for (ct = conf_root_table; ct->name; ct++)
      {
        if (!strcasecmp(ct->name, ce->ce_varname))
        {
          ct->handler(ce);
          break;
        }
      }
      if (ct->name == NULL)
      {
        slog(DEFAULT_LOG, L_WARNING, "%s:%i: invalid config option: %s",
             ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
             ce->ce_varname);
      }
    }
  }
  config_free(cfp);
}

void rehash(void)
{
  CONFIGFILE *cfptr;
  CONFIGFILE *cfp;
  CONFIGENTRY *ce;
  struct ConfTable *ct = NULL;
  dlink_node *dl;
  dlink_node *tdl;

  /* First things first...reset the bit flags to 0 */
  settings.flags = 0;
#ifdef SPLITSERV

  sp.flags = 0;
#endif
#ifdef STATSERV
  StatServ.flags = 0;
#endif
  /* Flush the hidden servers list */
  DLINK_FOREACH_SAFE(dl, tdl, hiddenhead.head)
  {
    struct Hidden *h = dl->data;
    dlink_delete(dl, &hiddenhead);
    free(h);
    memcounts.hidden--;
    dlink_free(dl);
  }

#ifdef MONSERV
  DLINK_FOREACH_SAFE(dl, tdl, MonServ.ignore_server_list.head)
  {
    char *s = dl->data;
    dlink_delete(dl, &MonServ.ignore_server_list);
    memcounts.ignoreserver--;
    memcounts.ignoreservermem -= strlen(s) + 1;
    free(s);
    dlink_free(dl);
  }
#ifdef MON_DRONES
  DLINK_FOREACH_SAFE(dl, tdl, MonServ.gecos_drone_list.head)
  {
    struct DroneEntry *de = dl->data;
    dlink_delete(dl, &MonServ.gecos_drone_list);
    memcounts.droneentry--;
    free(de);
    dlink_free(dl);
  }
  DLINK_FOREACH_SAFE(dl, tdl, MonServ.version_drone_list.head)
  {
    struct DroneEntry *de = dl->data;
    dlink_delete(dl, &MonServ.version_drone_list);
    memcounts.droneentry--;
    free(de);
    dlink_free(dl);
  }
  DLINK_FOREACH_SAFE(dl, tdl, MonServ.nick_drone_list.head)
  {
    struct DroneEntry *de = dl->data;
    dlink_delete(dl, &MonServ.nick_drone_list);
    memcounts.droneentry--;
    free(de);
    dlink_free(dl);
  }
  DLINK_FOREACH_SAFE(dl, tdl, MonServ.channel_drone_list.head)
  {
    struct DroneEntry *de = dl->data;
    dlink_delete(dl, &MonServ.channel_drone_list);
    memcounts.droneentry--;
    free(de);
    dlink_free(dl);
  }
  DLINK_FOREACH_SAFE(dl, tdl, MonServ.username_drone_list.head)
  {
    struct DroneEntry *de = dl->data;
    dlink_delete(dl, &MonServ.username_drone_list);
    memcounts.droneentry--;
    free(de);
    dlink_free(dl);
  }
#endif
#endif
  /* Wipe out the myu list */
  DLINK_FOREACH_SAFE(dl, tdl, myuhead.head)
  {
    struct MyUser *myu = (struct MyUser *) dl->data;

    if (IsOperOn(myu) || IsOperDCC(myu))
    {
      dlink_delete(dl, &myuhead);
      dlink_add(myu, dl, &myudying);
      myu->flags |= MFLAG_DYING;
    }
    else
    {
      dlink_delete(dl, &myuhead);
      dlink_free(dl);
#ifdef HAVE_LIBCRYPTO

      if (myu->response != NULL)
        free(myu->response);
      if (myu->keyfile != NULL)
        RSA_free(myu->keyfile);
#endif

      free(myu);
      memcounts.myuser--;
    }
  }

  cfptr = cfp = config_load(CONFIG_NAME);
  if (cfp == NULL)
  {
    slog(DEFAULT_LOG, L_CRIT, "Unable to open configuration file (%s): %s\n",
         CONFIG_NAME, strerror(errno));
  }
  for (;cfptr;cfptr = cfptr->cf_next) /* Handle include files? */
  {
    for (ce = cfptr->cf_entries;ce;ce = ce->ce_next)
    {
      for (ct = conf_root_table; ct->name; ct++)
      {
        if (!strcasecmp(ct->name, ce->ce_varname))
        {
          if (ct->rehashable == 1)
            ct->handler(ce);
          break;
        }
      }
      if (ct->name == NULL)
      {
        slog(DEFAULT_LOG, L_WARNING, "%s:%i: invalid config option: %s",
             ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
             ce->ce_varname);
      }
    }
  }
  config_free(cfp);

  VerifyConf();

  /* And finally (just in case the times changed) reset the
  ** conf time based events
  */
  ResetTimedEvents();
}

static int c_serverinfo(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "SERVERINFO", conf_serverinfo_table);
  return 0;
}

static int c_statserv(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "STATSERV", conf_statserv_table);
  return 0;
}

static int c_monserv(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "MONSERV", conf_monserv_table);
  return 0;
}

static int c_adminserv(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "ADMINSERV", conf_adminserv_table);
  return 0;
}

static int c_hidden(CONFIGENTRY *ce)
{
  if (ce->ce_entries == NULL)
  {
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: no entries for hidden block",
         ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
    return 1;
  }
  for (ce = ce->ce_entries;ce;ce = ce->ce_next)
  {
    AddHidden(ce->ce_varname, 1);
  }
  return 0;
}

static int c_flood(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "FLOOD", conf_flood_table);
  return 0;
}

static int c_html(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "HTML", conf_html_table);
  return 0;
}

static int c_datadump(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "DATADUMP", conf_datadump_table);
  return 0;
}

static int c_splitserv(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "SPLITSERV", conf_splitserv_table);
  return 0;
}

static int c_jupeserv(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "JUPESERV", conf_jupeserv_table);
  return 0;
}

static int c_listserv(CONFIGENTRY *ce)
{
  generic_subblock_handler(ce, "LISTSERV", conf_listserv_table);
  return 0;
}

static int generic_subblock_handler(CONFIGENTRY *ce, char *subblock,
                                    struct ConfTable *table)
{
  struct ConfTable *ct = NULL;

  for (ce = ce->ce_entries;ce;ce = ce->ce_next)
  {
    for (ct = table; ct->name; ct++)
    {
      if (!strcasecmp(ct->name, ce->ce_varname))
      {
        ct->handler(ce);
        break;
      }
    }
    if (ct->name == NULL)
    {
      slog(DEFAULT_LOG, L_WARNING, "%s:%i: invalid %s config option: %s",
           ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
           subblock, ce->ce_varname);
    }
  }
  return 0;
}

static int c_si_name(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  strlcpy(settings.name, ce->ce_vardata, sizeof(settings.name));
  return 0;
}

static int c_si_sid(CONFIGENTRY *ce)
{
	if (ce->ce_vardata == NULL)
	{
		PARAM_ERROR(ce, "serverinfo");
	}
	strlcpy(settings.sid, ce->ce_vardata, sizeof(settings.sid));
	return 0;
}

static int c_si_port(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  settings.port = ce->ce_vardatanum;
  return 0;
}

static int c_si_uplink(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  /* Jeremy found that [ipv6-ip] will core it (enclosing
  ** the IPv6 IP in [] seems to be a "standard" of IRC
  ** client programs on UNIX.  Extra processing here is to
  ** make sure it doesn't core...
  **
  ** This is sub-optimal...it should run through the
  ** param string and verify every character...
  */
  if (ce->ce_vardata[0] == '[')
  {
    int len;
    ce->ce_vardata++;
    len = strlen(ce->ce_vardata);
    if ((len > 0) && (ce->ce_vardata[len - 1] == ']'))
      ce->ce_vardata[len - 1] = '\0';
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: WARNING:  Brackets [] used in "
         "serverinfo uplink", ce->ce_fileptr->cf_filename,
         ce->ce_varlinenum);
  }

  strlcpy(settings.uplink, ce->ce_vardata, sizeof(settings.uplink));
  return 0;
}

static int c_si_pass(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  strlcpy(settings.pass, ce->ce_vardata, sizeof(settings.pass));
  return 0;
}

static int c_si_vhost(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  /* Jeremy's [ipv6-ip] core, look above for more info */
  if (ce->ce_vardata[0] == '[')
  {
    int len;
    ce->ce_vardata++;
    len = strlen(ce->ce_vardata);
    if ((len > 0) && (ce->ce_vardata[len - 1] == ']'))
      ce->ce_vardata[len - 1] = '\0';
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: WARNING:  Brackets [] used in "
         "serverinfo vhost", ce->ce_fileptr->cf_filename,
         ce->ce_varlinenum);
  }
  settings.pflags |= PFLAG_VHOST;
  strlcpy(settings.vhost, ce->ce_vardata, sizeof(settings.vhost));
  return 0;
}

static int c_si_desc(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  strlcpy(settings.desc, ce->ce_vardata, sizeof(settings.desc));
  return 0;
}

static int c_si_recontime(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  settings.r_time = ce->ce_vardatanum;
  return 0;
}

static int c_si_silent(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_SILENT;
  return 0;
}

static int c_si_notifyeob(CONFIGENTRY *ce)
{
#if defined(IRCD_HYB7) || defined(IRCD_BAHAMUT) || defined(IRCD_HYBSVS)
  settings.flags |= FLAG_NOTIFYEOB;
#else
  /* Nothing */
  ;
#endif

  return 0;
}

static int c_si_netname(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  strlcpy(settings.netname, ce->ce_vardata, sizeof(settings.netname));
  return 0;
}

static int c_si_adminname(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  strlcpy(settings.admin_name, ce->ce_vardata,
          sizeof(settings.admin_name));
  return 0;
}

static int c_si_adminemail(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  strlcpy(settings.admin_email, ce->ce_vardata,
          sizeof(settings.admin_email));
  return 0;
}

static int c_si_ignoreillegaltld(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_IGNORE_ILLEGAL_TLD;
  return 0;
}

static int c_si_loglevel(CONFIGENTRY *ce)
{
  int value;
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  value = token_to_value(log_levels, ce->ce_vardata);
  if ((value == TOKEN_UNMATCHED) || (value == TOKEN_ERROR))
  {
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: invalid value for serverinfo loglevel",
         ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
    return 1;
  }
  set_loglevel(DEFAULT_LOG, value);
  return 0;
}

static int c_si_localip(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "serverinfo");
  }
  strlcpy(me.local_ip_str, ce->ce_vardata, sizeof(me.local_ip_str));
  return 0;
}

static int c_st_nick(CONFIGENTRY *ce)
{
#ifdef STATSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "statserv");
  }
  strlcpy(StatServ.nick, ce->ce_vardata, sizeof(StatServ.nick));
#endif

  return 0;
}

static int c_st_user(CONFIGENTRY *ce)
{
#ifdef STATSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "statserv");
  }
  strlcpy(StatServ.user, ce->ce_vardata, sizeof(StatServ.user));
#endif

  return 0;
}

static int c_st_host(CONFIGENTRY *ce)
{
#ifdef STATSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "statserv");
  }
  strlcpy(StatServ.host, ce->ce_vardata, sizeof(StatServ.host));
#endif

  return 0;
}

static int c_st_desc(CONFIGENTRY *ce)
{
#ifdef STATSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "statserv");
  }
  strlcpy(StatServ.desc, ce->ce_vardata, sizeof(StatServ.desc));
#endif

  return 0;
}

static int c_st_operonly(CONFIGENTRY *ce)
{
#ifdef STATSERV
  StatServ.flags |= STFLAG_OPERONLY;
#endif

  return 0;
}

static int c_st_setoper(CONFIGENTRY *ce)
{
#ifdef STATSERV
  StatServ.flags |= STFLAG_SETOPER;
#endif

  return 0;
}

static int c_st_channels(CONFIGENTRY *ce)
{
#ifdef STATSERV
  if (ce->ce_entries == NULL)
  {
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: no entries for statserv config "
         "option: %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
         ce->ce_varname);
    return 1;
  }
  for (ce = ce->ce_entries;ce;ce = ce->ce_next)
  {
    AddStatsChannel(ce->ce_varname);
  }
#endif
  return 0;
}

static int c_st_publicchannel(CONFIGENTRY *ce)
{
#ifdef PUBSTATS
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "statserv");
  }
  strlcpy(settings.pub_chan, ce->ce_vardata, sizeof(settings.pub_chan));
#endif

  return 0;
}

static int c_st_publicdelayed(CONFIGENTRY *ce)
{
#ifdef PUBSTATS
  settings.flags |= FLAG_PUBDELAYED;
#endif

  return 0;
}

static int c_ms_nick(CONFIGENTRY *ce)
{
#ifdef MONSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv");
  }
  strlcpy(MonServ.nick, ce->ce_vardata, sizeof(MonServ.nick));
#endif

  return 0;
}

static int c_ms_user(CONFIGENTRY *ce)
{
#ifdef MONSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv");
  }
  strlcpy(MonServ.user, ce->ce_vardata, sizeof(MonServ.user));
#endif

  return 0;
}

static int c_ms_host(CONFIGENTRY *ce)
{
#ifdef MONSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv");
  }
  strlcpy(MonServ.host, ce->ce_vardata, sizeof(MonServ.host));
#endif

  return 0;
}

static int c_ms_desc(CONFIGENTRY *ce)
{
#ifdef MONSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv");
  }
  strlcpy(MonServ.desc, ce->ce_vardata, sizeof(MonServ.desc));
#endif

  return 0;
}

static int c_ms_ignoreservers(CONFIGENTRY *ce)
{
#ifdef MONSERV
  if (ce->ce_entries == NULL)
  {
    PARAM_ERROR(ce, "monserv");
  }
  for (ce = ce->ce_entries;ce;ce = ce->ce_next)
  {
    char * s = sstrdup(ce->ce_varname);
    dlink_node *dl = dlink_create();

    memcounts.ignoreserver++;
    memcounts.ignoreservermem += strlen(s) + 1; /* including \0 */
    dlink_add(s, dl, &MonServ.ignore_server_list);
  }
#endif
  return 0;
}

static int c_drones(CONFIGENTRY *ce)
{
#if defined(MONSERV) && defined(MON_DRONES)
  generic_subblock_handler(ce, "DRONES", conf_ms_drones_table);
#endif

  return 0;
}

static int c_as_nick(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "adminserv");
  }
  strlcpy(AdminServ.nick, ce->ce_vardata, sizeof(AdminServ.nick));
  return 0;
}

static int c_as_user(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "adminserv");
  }
  strlcpy(AdminServ.user, ce->ce_vardata, sizeof(AdminServ.user));
  return 0;
}

static int c_as_host(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "adminserv");
  }
  strlcpy(AdminServ.host, ce->ce_vardata, sizeof(AdminServ.host));
  return 0;
}

static int c_as_desc(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "adminserv");
  }
  strlcpy(AdminServ.desc, ce->ce_vardata, sizeof(AdminServ.desc));
  return 0;
}

static int c_as_operonly(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_AONLYOPERS;
  return 0;
}

static int c_as_spywallops(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_SPYWALLOPS;
  return 0;
}

static int c_as_opersadminonly(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_OPERSADMINONLY;
  return 0;
}

static int c_as_defaultlimit(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "adminserv");
  }
  settings.defaultlimit = ce->ce_vardatanum;
  return 0;
}

static int c_as_defaultflags(CONFIGENTRY *ce)
{
  if (ce->ce_entries == NULL)
  {
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: no entries for adminserv config "
         "option: %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
         ce->ce_varname);
    return 1;
  }
  for (ce = ce->ce_entries;ce;ce = ce->ce_next)
  {
    int val;

    val = token_to_value(flags_table, ce->ce_varname);
    if ((val != TOKEN_UNMATCHED) && (val != TOKEN_ERROR))
    {
      settings.default_flags |= val;
    }
    else
    {
      slog(DEFAULT_LOG, L_WARNING, "%s:%i: adminserv defaultflags: Unknown "
           "flag: %s", ce->ce_fileptr->cf_filename,
           ce->ce_varlinenum, ce->ce_varname);
    }

  }
  return 0;
}

static int c_as_unauthchat(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_UNAUTHCHAT;
  return 0;
}

static int c_as_dccoperonly(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_DCCOPERONLY;
  return 0;
}

static int c_as_dcclowport(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "adminserv");
  }
  settings.dcc_low_port = ce->ce_vardatanum;
  return 0;
}

static int c_as_dcchighport(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "adminserv");
  }
  settings.dcc_high_port = ce->ce_vardatanum;
  return 0;
}

static int c_fl_count(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "flood");
  }
  settings.flood = ce->ce_vardatanum;
  return 0;
}

static int c_fl_time(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "flood");
  }
  settings.flood_time = ce->ce_vardatanum;
  return 0;
}

static int c_fl_ignore(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_FLOOD;
  return 0;
}

static int c_ht_enable(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_HTML;
  return 0;
}

static int c_ht_filename(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "html");
  }
  strlcpy(settings.html, ce->ce_vardata, sizeof(settings.html));
  return 0;
}

static int c_ht_time(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "html");
  }
  settings.html_time = ce->ce_vardatanum;
  return 0;
}

static int c_ht_url(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "html");
  }
  strlcpy(settings.url, ce->ce_vardata, sizeof(settings.url));
  return 0;
}

static int c_ht_topchannum(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "html");
  }
  settings.topchannum = ce->ce_vardatanum;
  return 0;
}

static int c_dd_enable(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_DUMP;
  return 0;
}

static int c_dd_filename(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "datadump");
  }
  strlcpy(settings.dump_file, ce->ce_vardata, sizeof(settings.dump_file));
  return 0;
}

static int c_dd_time(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "datadump");
  }
  settings.dump_time = ce->ce_vardatanum;
  return 0;
}

static int c_sp_nick(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  strlcpy(SplitServ.nick, ce->ce_vardata, sizeof(SplitServ.nick));
#endif

  return 0;
}

static int c_sp_user(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  strlcpy(SplitServ.user, ce->ce_vardata, sizeof(SplitServ.user));
#endif

  return 0;
}

static int c_sp_host(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  strlcpy(SplitServ.host, ce->ce_vardata, sizeof(SplitServ.host));
#endif

  return 0;
}

static int c_sp_desc(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  strlcpy(sp.desc, ce->ce_vardata, sizeof(sp.desc));
#endif

  return 0;
}

static int c_sp_delay(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  sp.delay = ce->ce_vardatanum;
#endif

  return 0;
}

static int c_sp_mapfile(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  generic_subblock_handler(ce, "SPLITSERV MAPFILE",
                           conf_sp_mapfile_table);
#endif

  return 0;
}

static int c_sp_expiretime(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  sp.expire_time = ce->ce_vardatanum;
  if (sp.expire_time > 0)
    sp.flags |= SPF_EXPIRE;
#endif

  return 0;
}

static int c_sp_remindtime(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  sp.remind_time = ce->ce_vardatanum;
  if (sp.remind_time > 0)
    sp.flags |= SPF_REMIND;
#endif

  return 0;
}

static int c_sp_channel(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  strlcpy(sp.join_chan, ce->ce_vardata, sizeof(sp.join_chan));
#endif

  return 0;
}

static int c_sp_mapcommand(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  int value;
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  value = token_to_value(split_options, ce->ce_vardata);
  if ((value == TOKEN_UNMATCHED) || (value == TOKEN_ERROR))
  {
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: invalid value for splitserv mapcommand",
         ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
    return 1;
  }
  sp.map_setting = value;
#endif

  return 0;
}

static int c_sp_splitcommand(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  int value;
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  value = token_to_value(split_options, ce->ce_vardata);
  if ((value == TOKEN_UNMATCHED) || (value == TOKEN_ERROR))
  {
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: invalid value for splitserv splitcommand",
         ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
    return 1;
  }
  sp.split_setting = value;
#endif

  return 0;
}

static int c_sp_dumpcommand(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  int value;
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv");
  }
  value = token_to_value(split_options, ce->ce_vardata);
  if ((value == TOKEN_UNMATCHED) || (value == TOKEN_ERROR))
  {
    slog(DEFAULT_LOG, L_WARNING, "%s:%i: invalid value for splitserv dumpcommand",
         ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
    return 1;
  }
  sp.dump_setting = value;
#endif

  return 0;
}

static int c_sp_hidehidden(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  sp.flags |= SPF_HIDE;
#endif

  return 0;
}

static int c_sp_operonly(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  sp.flags |= SPF_OPERONLY;
#endif

  return 0;
}

static int c_sp_report(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  sp.flags |= SPF_REPORT;
#endif

  return 0;
}

static int c_sp_trackondisconnect(CONFIGENTRY *ce)
{
#ifdef SPLITSERV
  sp.flags |= SPF_TRACKONDISCONNECT;
#endif

  return 0;
}

static int c_js_nick(CONFIGENTRY *ce)
{
#ifdef JUPESERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "jupeserv");
  }
  strlcpy(JupeServ.nick, ce->ce_vardata, sizeof(JupeServ.nick));
#endif

  return 0;
}

static int c_js_user(CONFIGENTRY *ce)
{
#ifdef JUPESERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "jupeserv");
  }
  strlcpy(JupeServ.user, ce->ce_vardata, sizeof(JupeServ.user));
#endif

  return 0;
}

static int c_js_host(CONFIGENTRY *ce)
{
#ifdef JUPESERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "jupeserv");
  }
  strlcpy(JupeServ.host, ce->ce_vardata, sizeof(JupeServ.host));
#endif

  return 0;
}

static int c_js_desc(CONFIGENTRY *ce)
{
#ifdef JUPESERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "jupeserv");
  }
  strlcpy(JupeServ.desc, ce->ce_vardata, sizeof(JupeServ.desc));
#endif

  return 0;
}

static int c_js_persist(CONFIGENTRY *ce)
{
#ifdef JUPESERV
  settings.flags |= FLAG_JUPE_PERSIST;
#endif

  return 0;
}

static int c_js_alloper(CONFIGENTRY *ce)
{
#ifdef JUPESERV
  settings.flags |= FLAG_JUPE_ALLOPER;
#endif

  return 0;
}

static int c_js_votes(CONFIGENTRY *ce)
{
#ifdef JUPESERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "jupeserv");
  }
  settings.jupevotes = ce->ce_vardatanum;
#endif

  return 0;
}

static int c_ls_defaultmax(CONFIGENTRY *ce)
{
#ifdef LISTSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "listserv");
  }
  ListServ.default_max = ce->ce_vardatanum;
#endif

  return 0;
}

static int c_ls_nick(CONFIGENTRY *ce)
{
#ifdef LISTSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "listserv");
  }
  strlcpy(ListServ.nick, ce->ce_vardata, sizeof(ListServ.nick));
#endif

  return 0;
}

static int c_ls_user(CONFIGENTRY *ce)
{
#ifdef LISTSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "listserv");
  }
  strlcpy(ListServ.user, ce->ce_vardata, sizeof(ListServ.user));
#endif

  return 0;
}

static int c_ls_host(CONFIGENTRY *ce)
{
#ifdef LISTSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "listserv");
  }
  strlcpy(ListServ.host, ce->ce_vardata, sizeof(ListServ.host));
#endif

  return 0;
}

static int c_ls_desc(CONFIGENTRY *ce)
{
#ifdef LISTSERV
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "listserv");
  }
  strlcpy(ListServ.desc, ce->ce_vardata, sizeof(ListServ.desc));
#endif

  return 0;
}

#ifdef SPLITSERV
static int c_sp_mf_enable(CONFIGENTRY *ce)
{
  sp.flags |= SPF_ENABLE_TEXT;
  return 0;
}

static int c_sp_mf_filename(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv mapfile");
  }
  strlcpy(sp.text_name, ce->ce_vardata, sizeof(sp.text_name));
  return 0;
}

static int c_sp_mf_time(CONFIGENTRY *ce)
{
  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "splitserv mapfile");
  }
  sp.text_time = ce->ce_vardatanum;
  return 0;
}
#endif

#if defined(MONSERV) && defined(MON_DRONES)
static int c_ms_dr_gecos(CONFIGENTRY *ce)
{
  dlink_node *dl;
  struct DroneEntry *de;

  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv drones");
  }
  dl = dlink_create();
  de = smalloc(sizeof(struct DroneEntry));
  memcounts.droneentry++;
  strlcpy(de->name, ce->ce_vardata, MAXUSERHOST);
  dlink_add(de, dl, &MonServ.gecos_drone_list);
  return 0;
}

static int c_ms_dr_nick(CONFIGENTRY *ce)
{
  dlink_node *dl;
  struct DroneEntry *de;

  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv drones");
  }
  dl = dlink_create();
  de = smalloc(sizeof(struct DroneEntry));
  memcounts.droneentry++;
  strlcpy(de->name, ce->ce_vardata, MAXUSERHOST);
  dlink_add(de, dl, &MonServ.nick_drone_list);
  return 0;
}

static int c_ms_dr_channel(CONFIGENTRY *ce)
{
  dlink_node *dl;
  struct DroneEntry *de;

  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv drones");
  }
  dl = dlink_create();
  de = smalloc(sizeof(struct DroneEntry));
  memcounts.droneentry++;
  strlcpy(de->name, ce->ce_vardata, MAXUSERHOST);
  dlink_add(de, dl, &MonServ.channel_drone_list);
  return 0;
}

static int c_ms_dr_username(CONFIGENTRY *ce)
{
  dlink_node *dl;
  struct DroneEntry *de;

  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv drones");
  }
  dl = dlink_create();
  de = smalloc(sizeof(struct DroneEntry));
  memcounts.droneentry++;
  strlcpy(de->name, ce->ce_vardata, MAXUSERHOST);
  dlink_add(de, dl, &MonServ.username_drone_list);
  return 0;
}

static int c_ms_dr_version(CONFIGENTRY *ce)
{
  dlink_node *dl;
  struct DroneEntry *de;

  if (ce->ce_vardata == NULL)
  {
    PARAM_ERROR(ce, "monserv drones");
  }
  dl = dlink_create();
  de = smalloc(sizeof(struct DroneEntry));
  memcounts.droneentry++;
  strlcpy(de->name, ce->ce_vardata, MAXUSERHOST);
  dlink_add(de, dl, &MonServ.version_drone_list);
  return 0;
}

static int c_ms_dr_nickgecos(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_MON_NG;
  return 0;
}

static int c_ms_dr_trackfizzer(CONFIGENTRY *ce)
{
  settings.flags |= FLAG_TRACKFIZZER;
  return 0;
}
#endif

static int c_operator(CONFIGENTRY *ce)
{
  struct MyUser *myu;
  dlink_node *dl;
  CONFIGENTRY *flce;

  myu = smalloc(sizeof(struct MyUser));
  memcounts.myuser++;

  if (ce->ce_vardata != NULL)
  {
    /* Someone passed us an operator Sarisa { ... */
    strlcpy(myu->username, ce->ce_vardata, MAXNICK + 1);
  }

  for (ce = ce->ce_entries;ce;ce = ce->ce_next)
  {
    if (!strcasecmp(ce->ce_varname, "NAME"))
    {
      if (ce->ce_vardata == NULL)
      {
        slog(DEFAULT_LOG, L_WARNING, "%s:%i: no parameter for "
             "operator config option: %s",
             ce->ce_fileptr->cf_filename,
             ce->ce_varlinenum, ce->ce_varname);
        free(myu);
        memcounts.myuser--;
        return 1;
      }
      strlcpy(myu->username, ce->ce_vardata, MAXNICK + 1);
      continue;
    }
    else if (!strcasecmp(ce->ce_varname, "PASSWORD"))
    {
      if (ce->ce_vardata == NULL)
      {
        slog(DEFAULT_LOG, L_WARNING, "%s:%i: no parameter for "
             "operator config option: %s",
             ce->ce_fileptr->cf_filename,
             ce->ce_varlinenum, ce->ce_varname);
        free(myu);
        memcounts.myuser--;
        return 1;
      }
      strlcpy(myu->password, ce->ce_vardata, LINE);
      continue;
    }
    else if (!strcasecmp(ce->ce_varname, "KEYFILE"))
    {
#ifdef HAVE_LIBCRYPTO
      BIO *file;

      if (ce->ce_vardata == NULL)
      {
        slog(DEFAULT_LOG, L_WARNING, "%s:%i: no parameter for "
             "operator config option: %s",
             ce->ce_fileptr->cf_filename,
             ce->ce_varlinenum, ce->ce_varname);
        free(myu);
        memcounts.myuser--;
        return 1;
      }
      file = BIO_new_file(ce->ce_vardata, "r");

      if (file == NULL)
      {
        slog(DEFAULT_LOG, L_WARNING, "%s:%i: keyfile %s not found",
             ce->ce_fileptr->cf_filename,
             ce->ce_varlinenum, ce->ce_vardata);
        free(myu);
        memcounts.myuser--;
        return 1;
      }
      myu->keyfile = (RSA *) PEM_read_bio_RSA_PUBKEY(file,
                     NULL, 0, NULL);
      if (myu->keyfile == NULL)
      {
        slog(DEFAULT_LOG, L_WARNING, "%s:%i: keyfile %s invalid",
             ce->ce_fileptr->cf_filename,
             ce->ce_varlinenum, ce->ce_vardata);
        free(myu);
        memcounts.myuser--;
        return 1;
      }
#endif

    }
    else if (!strcasecmp(ce->ce_varname, "FLAGS"))
    {
      for (flce = ce->ce_entries; flce; flce = flce->ce_next)
      {
        int val;
        val = token_to_value(oper_flags,
                             flce->ce_varname);
        if ((val != TOKEN_UNMATCHED) &&
            (val != TOKEN_ERROR))
        {
          myu->flags |= val;
        }
        else
        {
          slog(DEFAULT_LOG, L_WARNING, "%s:%i: operator flags: "
               "Unknown flag: %s",
               flce->ce_fileptr->cf_filename,
               flce->ce_varlinenum,
               flce->ce_varname);
        }
      }
    }
    else
    {
      slog(DEFAULT_LOG, L_WARNING, "%s:%i: invalid operator config option: "
           "%s", ce->ce_fileptr->cf_filename,
           ce->ce_varlinenum, ce->ce_varname);
      continue;
    }
  }
  /* Verification */
  if (myu->username[0] == '\0')
  {
    /* Can't give any more without causing cores :( */
    slog(DEFAULT_LOG, L_WARNING, "incomplete operator block");
    free(myu);
    memcounts.myuser--;
    return 1;
  }
  dl = dlink_create();
  dlink_add(myu, dl, &myuhead);
  return 0;
}
